// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/base/audio_discard_helper.h"

#include <stddef.h>

#include <memory>

#include "media/base/audio_buffer.h"
#include "media/base/audio_bus.h"
#include "media/base/decoder_buffer.h"
#include "media/base/test_helpers.h"
#include "media/base/timestamp_constants.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace media {

static const float kDataStep = 0.01f;
static const size_t kSampleRate = 48000;

static scoped_refptr<DecoderBuffer> CreateEncodedBuffer(
    base::TimeDelta timestamp,
    base::TimeDelta duration)
{
    scoped_refptr<DecoderBuffer> result(new DecoderBuffer(1));
    result->set_timestamp(timestamp);
    result->set_duration(duration);
    return result;
}

static scoped_refptr<AudioBuffer> CreateDecodedBuffer(int frames)
{
    return MakeAudioBuffer(kSampleFormatPlanarF32, CHANNEL_LAYOUT_MONO, 1,
        kSampleRate, 0.0f, kDataStep, frames, kNoTimestamp);
}

static float ExtractDecodedData(const scoped_refptr<AudioBuffer>& buffer,
    int index)
{
    // This is really inefficient, but we can't access the raw AudioBuffer if any
    // start trimming has been applied.
    std::unique_ptr<AudioBus> temp_bus = AudioBus::Create(buffer->channel_count(), 1);
    buffer->ReadFrames(1, index, 0, temp_bus.get());
    return temp_bus->channel(0)[0];
}

TEST(AudioDiscardHelperTest, TimeDeltaToFrames)
{
    AudioDiscardHelper discard_helper(kSampleRate, 0, false);

    EXPECT_EQ(0u, discard_helper.TimeDeltaToFrames(base::TimeDelta()));
    EXPECT_EQ(
        kSampleRate / 100,
        discard_helper.TimeDeltaToFrames(base::TimeDelta::FromMilliseconds(10)));

    // Ensure partial frames are rounded down correctly.  The equation below
    // calculates a frame count with a fractional part < 0.5.
    const int small_remainder = base::Time::kMicrosecondsPerSecond * (kSampleRate - 0.9) / kSampleRate;
    EXPECT_EQ(kSampleRate - 1,
        discard_helper.TimeDeltaToFrames(
            base::TimeDelta::FromMicroseconds(small_remainder)));

    // Ditto, but rounded up using a fractional part > 0.5.
    const int large_remainder = base::Time::kMicrosecondsPerSecond * (kSampleRate - 0.4) / kSampleRate;
    EXPECT_EQ(kSampleRate,
        discard_helper.TimeDeltaToFrames(
            base::TimeDelta::FromMicroseconds(large_remainder)));
}

TEST(AudioDiscardHelperTest, BasicProcessBuffers)
{
    AudioDiscardHelper discard_helper(kSampleRate, 0, false);
    ASSERT_FALSE(discard_helper.initialized());

    const base::TimeDelta kTimestamp = base::TimeDelta();

    // Use an estimated duration which doesn't match the number of decoded frames
    // to ensure the helper is correctly setting durations based on output frames.
    const base::TimeDelta kEstimatedDuration = base::TimeDelta::FromMilliseconds(9);
    const base::TimeDelta kActualDuration = base::TimeDelta::FromMilliseconds(10);
    const int kTestFrames = discard_helper.TimeDeltaToFrames(kActualDuration);

    scoped_refptr<DecoderBuffer> encoded_buffer = CreateEncodedBuffer(kTimestamp, kEstimatedDuration);
    scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

    // Verify the basic case where nothing is discarded.
    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    ASSERT_TRUE(discard_helper.initialized());
    EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
    EXPECT_EQ(kActualDuration, decoded_buffer->duration());
    EXPECT_EQ(kTestFrames, decoded_buffer->frame_count());

    // Verify a Reset() takes us back to an uninitialized state.
    discard_helper.Reset(0);
    ASSERT_FALSE(discard_helper.initialized());

    // Verify a NULL output buffer returns false.
    ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, NULL));
}

TEST(AudioDiscardHelperTest, NegativeTimestampClampsToZero)
{
    AudioDiscardHelper discard_helper(kSampleRate, 0, false);
    ASSERT_FALSE(discard_helper.initialized());

    const base::TimeDelta kTimestamp = -base::TimeDelta::FromSeconds(1);
    const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
    const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);

    scoped_refptr<DecoderBuffer> encoded_buffer = CreateEncodedBuffer(kTimestamp, kDuration);
    scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

    // Verify the basic case where nothing is discarded.
    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    ASSERT_TRUE(discard_helper.initialized());
    EXPECT_EQ(base::TimeDelta(), decoded_buffer->timestamp());
    EXPECT_EQ(kDuration, decoded_buffer->duration());
    EXPECT_EQ(kTestFrames, decoded_buffer->frame_count());
}

TEST(AudioDiscardHelperTest, ProcessBuffersWithInitialDiscard)
{
    AudioDiscardHelper discard_helper(kSampleRate, 0, false);
    ASSERT_FALSE(discard_helper.initialized());

    const base::TimeDelta kTimestamp = base::TimeDelta();
    const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
    const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);

    // Tell the helper we want to discard half of the initial frames.
    const int kDiscardFrames = kTestFrames / 2;
    discard_helper.Reset(kDiscardFrames);

    scoped_refptr<DecoderBuffer> encoded_buffer = CreateEncodedBuffer(kTimestamp, kDuration);
    scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

    // Verify half the frames end up discarded.
    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    ASSERT_TRUE(discard_helper.initialized());
    EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
    EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
    EXPECT_EQ(kDiscardFrames, decoded_buffer->frame_count());
    ASSERT_FLOAT_EQ(kDiscardFrames * kDataStep,
        ExtractDecodedData(decoded_buffer, 0));
}

TEST(AudioDiscardHelperTest, ProcessBuffersWithLargeInitialDiscard)
{
    AudioDiscardHelper discard_helper(kSampleRate, 0, false);
    ASSERT_FALSE(discard_helper.initialized());

    const base::TimeDelta kTimestamp = base::TimeDelta();
    const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
    const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);

    // Tell the helper we want to discard 1.5 buffers worth of frames.
    discard_helper.Reset(kTestFrames * 1.5);

    scoped_refptr<DecoderBuffer> encoded_buffer = CreateEncodedBuffer(kTimestamp, kDuration);
    scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

    // The first call should fail since no output buffer remains.
    ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    ASSERT_TRUE(discard_helper.initialized());

    // Generate another set of buffers and expect half the output frames.
    encoded_buffer = CreateEncodedBuffer(kTimestamp + kDuration, kDuration);
    decoded_buffer = CreateDecodedBuffer(kTestFrames);
    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));

    // The timestamp should match that of the initial buffer.
    const int kDiscardFrames = kTestFrames / 2;
    EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
    EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
    EXPECT_EQ(kDiscardFrames, decoded_buffer->frame_count());
    ASSERT_FLOAT_EQ(kDiscardFrames * kDataStep,
        ExtractDecodedData(decoded_buffer, 0));
}

TEST(AudioDiscardHelperTest, AllowNonMonotonicTimestamps)
{
    AudioDiscardHelper discard_helper(kSampleRate, 0, false);
    ASSERT_FALSE(discard_helper.initialized());

    const base::TimeDelta kTimestamp = base::TimeDelta();
    const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
    const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);

    scoped_refptr<DecoderBuffer> encoded_buffer = CreateEncodedBuffer(kTimestamp, kDuration);
    scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    ASSERT_TRUE(discard_helper.initialized());
    EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
    EXPECT_EQ(kDuration, decoded_buffer->duration());
    EXPECT_EQ(kTestFrames, decoded_buffer->frame_count());

    // Process the same input buffer again to ensure input timestamps which go
    // backwards in time are not errors.
    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    EXPECT_EQ(kTimestamp + kDuration, decoded_buffer->timestamp());
    EXPECT_EQ(kDuration, decoded_buffer->duration());
    EXPECT_EQ(kTestFrames, decoded_buffer->frame_count());
}

TEST(AudioDiscardHelperTest, DiscardEndPadding)
{
    AudioDiscardHelper discard_helper(kSampleRate, 0, false);
    ASSERT_FALSE(discard_helper.initialized());

    const base::TimeDelta kTimestamp = base::TimeDelta();
    const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
    const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);

    scoped_refptr<DecoderBuffer> encoded_buffer = CreateEncodedBuffer(kTimestamp, kDuration);
    scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

    // Set a discard padding equivalent to half the buffer.
    encoded_buffer->set_discard_padding(
        std::make_pair(base::TimeDelta(), kDuration / 2));

    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    ASSERT_TRUE(discard_helper.initialized());
    EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
    EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
    EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
}

TEST(AudioDiscardHelperTest, BadDiscardEndPadding)
{
    AudioDiscardHelper discard_helper(kSampleRate, 0, false);
    ASSERT_FALSE(discard_helper.initialized());

    const base::TimeDelta kTimestamp = base::TimeDelta();
    const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
    const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);

    scoped_refptr<DecoderBuffer> encoded_buffer = CreateEncodedBuffer(kTimestamp, kDuration);
    scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

    // Set a discard padding equivalent to double the buffer size.
    encoded_buffer->set_discard_padding(
        std::make_pair(base::TimeDelta(), kDuration * 2));

    // Verify the end discard padding is rejected.
    ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    ASSERT_TRUE(discard_helper.initialized());
}

TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardEndPadding)
{
    AudioDiscardHelper discard_helper(kSampleRate, 0, false);
    ASSERT_FALSE(discard_helper.initialized());

    const base::TimeDelta kTimestamp = base::TimeDelta();
    const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
    const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);

    scoped_refptr<DecoderBuffer> encoded_buffer = CreateEncodedBuffer(kTimestamp, kDuration);
    scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

    // Set a discard padding equivalent to a quarter of the buffer.
    encoded_buffer->set_discard_padding(
        std::make_pair(base::TimeDelta(), kDuration / 4));

    // Set an initial discard of a quarter of the buffer.
    const int kDiscardFrames = kTestFrames / 4;
    discard_helper.Reset(kDiscardFrames);

    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    ASSERT_TRUE(discard_helper.initialized());
    EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
    EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
    EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
    ASSERT_FLOAT_EQ(kDiscardFrames * kDataStep,
        ExtractDecodedData(decoded_buffer, 0));
}

TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardPadding)
{
    AudioDiscardHelper discard_helper(kSampleRate, 0, false);
    ASSERT_FALSE(discard_helper.initialized());

    const base::TimeDelta kTimestamp = base::TimeDelta();
    const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
    const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);

    scoped_refptr<DecoderBuffer> encoded_buffer = CreateEncodedBuffer(kTimestamp, kDuration);
    scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

    // Set all the discard values to be different to ensure each is properly used.
    const int kDiscardFrames = kTestFrames / 4;
    encoded_buffer->set_discard_padding(
        std::make_pair(kDuration / 8, kDuration / 16));
    discard_helper.Reset(kDiscardFrames);

    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    ASSERT_TRUE(discard_helper.initialized());
    EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
    EXPECT_EQ(kDuration - kDuration / 4 - kDuration / 8 - kDuration / 16,
        decoded_buffer->duration());
    EXPECT_EQ(kTestFrames - kTestFrames / 4 - kTestFrames / 8 - kTestFrames / 16,
        decoded_buffer->frame_count());
}

TEST(AudioDiscardHelperTest, InitialDiscardAndDiscardPaddingAndDecoderDelay)
{
    // Use a decoder delay of 5ms.
    const int kDecoderDelay = kSampleRate / 100 / 2;
    AudioDiscardHelper discard_helper(kSampleRate, kDecoderDelay, false);
    ASSERT_FALSE(discard_helper.initialized());
    discard_helper.Reset(kDecoderDelay);

    const base::TimeDelta kTimestamp = base::TimeDelta();
    const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
    const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);

    scoped_refptr<DecoderBuffer> encoded_buffer = CreateEncodedBuffer(kTimestamp, kDuration);
    scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

    // Set a discard padding equivalent to half of the buffer.
    encoded_buffer->set_discard_padding(
        std::make_pair(kDuration / 2, base::TimeDelta()));

    // All of the first buffer should be discarded, half from the inital delay and
    // another half from the front discard padding.
    //
    //    Encoded                   Discard Delay
    //   |--------|     |---------|     |----|
    //   |AAAAAAAA| --> |....|AAAA| --> |AAAA| -------> NULL
    //   |--------|     |---------|     |----|
    //                    Decoded               Discard Front Padding
    //
    ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    ASSERT_TRUE(discard_helper.initialized());

    // Processing another buffer that has front discard set to half the buffer's
    // duration should discard the back half of the buffer since kDecoderDelay is
    // half a buffer.  The end padding should not be discarded until another
    // buffer is processed.  kDuration / 4 is chosen for the end discard since it
    // will force the end discard to start after position zero within the next
    // decoded buffer.
    //
    //    Encoded                    Discard Front Padding (from B)
    //   |--------|     |---------|             |----|
    //   |BBBBBBBB| --> |AAAA|BBBB| ----------> |AAAA|
    //   |--------|     |---------|             |----|
    //                    Decoded
    //           (includes carryover from A)
    //
    encoded_buffer->set_timestamp(encoded_buffer->timestamp() + kDuration);
    encoded_buffer->set_discard_padding(
        std::make_pair(kDuration / 2, kDuration / 4));
    decoded_buffer = CreateDecodedBuffer(kTestFrames);
    ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
    ASSERT_NEAR(kDecoderDelay * kDataStep,
        ExtractDecodedData(decoded_buffer, kDecoderDelay),
        kDataStep / 1000);
    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
    EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
    EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());

    // Verify it was actually the latter half of the buffer that was removed.
    ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));

    // Verify the end discard padding is carried over to the next buffer.  Use
    // kDuration / 2 for the end discard padding so that the next buffer has its
    // start entirely discarded.
    //
    //    Encoded                      Discard End Padding (from B)
    //   |--------|     |---------|             |-------|
    //   |CCCCCCCC| --> |BBBB|CCCC| ----------> |BB|CCCC|
    //   |--------|     |---------|             |-------|
    //                    Decoded
    //           (includes carryover from B)
    //
    encoded_buffer->set_timestamp(encoded_buffer->timestamp() + kDuration);
    encoded_buffer->set_discard_padding(
        std::make_pair(base::TimeDelta(), kDuration / 2));
    decoded_buffer = CreateDecodedBuffer(kTestFrames);
    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    EXPECT_EQ(kTimestamp + kDuration / 2, decoded_buffer->timestamp());
    EXPECT_EQ(3 * kDuration / 4, decoded_buffer->duration());
    EXPECT_EQ(3 * kTestFrames / 4, decoded_buffer->frame_count());

    // Verify it was actually the second quarter of the buffer that was removed.
    const int kDiscardFrames = kTestFrames / 4;
    ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
    ASSERT_FLOAT_EQ(
        kDiscardFrames * 2 * kDataStep,
        ExtractDecodedData(decoded_buffer, kDecoderDelay - kDiscardFrames));

    // One last test to ensure carryover discard from the start works.
    //
    //    Encoded                      Discard End Padding (from C)
    //   |--------|     |---------|             |----|
    //   |DDDDDDDD| --> |CCCC|DDDD| ----------> |DDDD|
    //   |--------|     |---------|             |----|
    //                    Decoded
    //           (includes carryover from C)
    //
    encoded_buffer->set_timestamp(encoded_buffer->timestamp() + kDuration);
    encoded_buffer->set_discard_padding(DecoderBuffer::DiscardPadding());
    decoded_buffer = CreateDecodedBuffer(kTestFrames);
    ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    EXPECT_EQ(kTimestamp + kDuration / 2 + 3 * kDuration / 4,
        decoded_buffer->timestamp());
    EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
    EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
    ASSERT_FLOAT_EQ(kTestFrames / 2 * kDataStep,
        ExtractDecodedData(decoded_buffer, 0));
}

TEST(AudioDiscardHelperTest, DelayedDiscardInitialDiscardAndDiscardPadding)
{
    AudioDiscardHelper discard_helper(kSampleRate, 0, true);
    ASSERT_FALSE(discard_helper.initialized());

    const base::TimeDelta kTimestamp = base::TimeDelta();
    const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
    const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);

    scoped_refptr<DecoderBuffer> encoded_buffer = CreateEncodedBuffer(kTimestamp, kDuration);

    // Set all the discard values to be different to ensure each is properly used.
    const int kDiscardFrames = kTestFrames / 4;
    encoded_buffer->set_discard_padding(
        std::make_pair(kDuration / 8, kDuration / 16));
    discard_helper.Reset(kDiscardFrames);

    // Verify nothing is output for the first buffer, yet initialized is true.
    ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, NULL));
    ASSERT_TRUE(discard_helper.initialized());

    // Create an encoded buffer with no discard padding.
    encoded_buffer = CreateEncodedBuffer(kTimestamp + kDuration, kDuration);
    scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

    // Verify that when the decoded buffer is consumed, the discards from the
    // previous encoded buffer are applied.
    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
    EXPECT_EQ(kDuration - kDuration / 4 - kDuration / 8 - kDuration / 16,
        decoded_buffer->duration());
    EXPECT_EQ(kTestFrames - kTestFrames / 4 - kTestFrames / 8 - kTestFrames / 16,
        decoded_buffer->frame_count());
}

TEST(AudioDiscardHelperTest, CompleteDiscard)
{
    AudioDiscardHelper discard_helper(kSampleRate, 0, false);
    ASSERT_FALSE(discard_helper.initialized());

    const base::TimeDelta kTimestamp = base::TimeDelta();
    const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
    const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
    discard_helper.Reset(0);

    scoped_refptr<DecoderBuffer> encoded_buffer = CreateEncodedBuffer(kTimestamp, kDuration);
    encoded_buffer->set_discard_padding(
        std::make_pair(kInfiniteDuration, base::TimeDelta()));
    scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

    // Verify all of the first buffer is discarded.
    ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    ASSERT_TRUE(discard_helper.initialized());
    encoded_buffer->set_timestamp(kTimestamp + kDuration);
    encoded_buffer->set_discard_padding(DecoderBuffer::DiscardPadding());

    // Verify a second buffer goes through untouched.
    decoded_buffer = CreateDecodedBuffer(kTestFrames / 2);
    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
    EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
    EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
    ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
}

TEST(AudioDiscardHelperTest, CompleteDiscardWithDelayedDiscard)
{
    AudioDiscardHelper discard_helper(kSampleRate, 0, true);
    ASSERT_FALSE(discard_helper.initialized());

    const base::TimeDelta kTimestamp = base::TimeDelta();
    const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
    const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);
    discard_helper.Reset(0);

    scoped_refptr<DecoderBuffer> encoded_buffer = CreateEncodedBuffer(kTimestamp, kDuration);
    encoded_buffer->set_discard_padding(
        std::make_pair(kInfiniteDuration, base::TimeDelta()));
    scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

    // Setup a delayed discard.
    ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, NULL));
    ASSERT_TRUE(discard_helper.initialized());

    // Verify the first output buffer is dropped.
    encoded_buffer->set_timestamp(kTimestamp + kDuration);
    encoded_buffer->set_discard_padding(DecoderBuffer::DiscardPadding());
    ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));

    // Verify the second buffer goes through untouched.
    encoded_buffer->set_timestamp(kTimestamp + 2 * kDuration);
    decoded_buffer = CreateDecodedBuffer(kTestFrames / 2);
    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
    EXPECT_EQ(kDuration / 2, decoded_buffer->duration());
    EXPECT_EQ(kTestFrames / 2, decoded_buffer->frame_count());
    ASSERT_FLOAT_EQ(0.0f, ExtractDecodedData(decoded_buffer, 0));
}

TEST(AudioDiscardHelperTest, CompleteDiscardWithInitialDiscardDecoderDelay)
{
    // Use a decoder delay of 5ms.
    const int kDecoderDelay = kSampleRate / 100 / 2;
    AudioDiscardHelper discard_helper(kSampleRate, kDecoderDelay, false);
    ASSERT_FALSE(discard_helper.initialized());
    discard_helper.Reset(kDecoderDelay);

    const base::TimeDelta kTimestamp = base::TimeDelta();
    const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(10);
    const int kTestFrames = discard_helper.TimeDeltaToFrames(kDuration);

    scoped_refptr<DecoderBuffer> encoded_buffer = CreateEncodedBuffer(kTimestamp, kDuration);
    encoded_buffer->set_discard_padding(
        std::make_pair(kInfiniteDuration, base::TimeDelta()));
    scoped_refptr<AudioBuffer> decoded_buffer = CreateDecodedBuffer(kTestFrames);

    // Verify all of the first buffer is discarded.
    ASSERT_FALSE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    ASSERT_TRUE(discard_helper.initialized());
    encoded_buffer->set_timestamp(kTimestamp + kDuration);
    encoded_buffer->set_discard_padding(DecoderBuffer::DiscardPadding());

    // Verify 5ms off the front of the second buffer is discarded.
    decoded_buffer = CreateDecodedBuffer(kTestFrames * 2);
    ASSERT_TRUE(discard_helper.ProcessBuffers(encoded_buffer, decoded_buffer));
    EXPECT_EQ(kTimestamp, decoded_buffer->timestamp());
    EXPECT_EQ(kDuration * 2 - kDuration / 2, decoded_buffer->duration());
    EXPECT_EQ(kTestFrames * 2 - kDecoderDelay, decoded_buffer->frame_count());
    ASSERT_FLOAT_EQ(kDecoderDelay * kDataStep,
        ExtractDecodedData(decoded_buffer, 0));
}

} // namespace media
